/*
Abhängigkeiten und globale Variablen werden definiert
*/
var application_root = __dirname,
    clients = [],
    express = require('express'), //Webframework
    app = express(),
    bodyParser = require('body-parser'), //parser for reading request body
    path = require('path'), //Utilities for dealing with file paths
    mongoose = require('mongoose'), //MongoDB integration
    server = require('http').createServer(app),
    io = require('socket.io').listen(server), //Socket IO
    port = 8080;

//Connect to database
mongoose.connect('mongodb://localhost/tournament_database');


//Schemas
var Tournament = new mongoose.Schema({
    tournament: Object,
    password: String
});

//Models
var TournamentModel = mongoose.model('Tournament', Tournament);

/*
Server-Konfiguration
*/

//parses request body and populates request.body
app.use(express.bodyParser());
//checks request.body for HTTP method overrides
app.use(express.methodOverride());
//perform route lookup based on url and HTTP method
app.use(app.router);
//Where to serve static content
app.use(express.static(application_root + '/www'));

//Show all errors in development
app.use(express.errorHandler({
    dumpExceptions: true,
    showStack: true
}));

/*
Websocket
*/

var socketFunctions = {
    socketHandling: function(socket) {
        var client = {};

        socket.on('register', function(data) {

            if (!(client = clients[data.uuid])) { // New client
                console.log('> New client');
                client = clients[data.uuid] = {
                    uuid: data.uuid,
                    tournamentID: data.currentTournament || -1,
                    isAdmin: false,
                    socket: socket
                };
            } else {
                client.socket = socket;
                // Check for multiple browser tabs opened
                if (!client.disconnected) {
                    console.log("ACHTUNG, HIER SOLLTE NICHT REIN GESPRUNGEN WERDEN!!");
                    socket.disconnect();
                }
            }

            // Keep client connected if a refresh occur before timeout
            if (client.disconnected) {
                clearTimeout(client.timeout);
                client.disconnected = false;
            }

            if (client.tournamentID != -1) {
                TournamentModel.findById(client.tournamentID, function(err, tItem) {
                    if (!err) {
                        socketFunctions.joinTournament(client);
                        //Turnier senden?? 
                    } else {
                        console.log("Turnier nicht gefunden: " + err);
                        console.log("Aufruf: initNewClient");
                        //Listener Eitragen
                        socketFunctions.refreshListener(client);
                        //wird in die Gruppe der Übersicht eingetragen
                        socket.join('lounge');
                        // Ihm wird die aktuelle Liste aller laufenden Turniere zur Verfügung gestellt
                        socket.emit('errorMessage', {
                            msg: 'Turnier wurde nicht gefunden.'
                        });
                    }
                });
            } else {
                console.log("Aufruf: initNewClient");
                //Listener Eitragen
                socketFunctions.refreshListener(client);
                //wird in die Gruppe der Übersicht eingetragen
                socket.join('lounge');
            }
            clients[data.uuid] = client;
        });

        socket.on('disconnect', function(type) {

            client.disconnected = true;

            client.timeout = setTimeout(function() {
                if (client.disconnected) {
                    delete clients[client.uuid];
                    console.log('> Client disconnected');
                }
            }, 2000);
        });
    },

    joinTournament: function(client) {
        console.log("Aufruf: joinTournament");
        try {
            if (client.tournamentID) {
                //Listener anpassen
                socketFunctions.refreshListener(client);
                console.log("--> der ID " + client.tournamentID);
                //Sicherstellen dass nur ein zusätzlicher raum betreten wird
                if (client.socket.rooms.length > 1) {
                    client.socket.leave(client.socket.rooms.pop());
                }
                //Raum beitreten
                client.socket.join(client.tournamentID);
            }
        } catch (err) {
            console.log(err);
        }
    },

    leaveTournament: function(client) {
        console.log("Aufruf: Leave Tournament");
        try {
            //Listeneranpassen
            client.tournamentID = -1;
            client.isAdmin = false;
            socketFunctions.refreshListener(client);
            //socket aus gruppe austragen
            if (client.socket.rooms.length > 1) {
                client.socket.leave(client.socket.rooms.pop());
            }
            //in gruppe lounge eintragen
            client.socket.join('lounge');
        } catch (err) {
            console.log(err);
        }
    },

    refreshListener: function(client) {
        console.log('Refresh Listener');
        try {
            client.socket.removeAllListeners('leaveTournament');
            if (client.tournamentID != -1) {
                //Listener
                client.socket.on('leaveTournament', function() {
                    socketFunctions.leaveTournament(client);
                });
            }
        } catch (err) {
            console.log('Err Refresh Listener: ' + err);
        }
    }
};

app.get('/', function(req, res) {
    res.sendfile(application_root + '/www/index.html');
});

app.post('/tournamentList', function(request, response) {
    // Ihm wird die aktuelle Liste aller laufenden Turniere zur Verfügung gestellt
    var query = TournamentModel.find({}, {
        "tournament.name": 1
    });
    query.exec(function(err, list) {
        if (!err) {
            return response.send({
                list: list
            });
        } else {
            console.log(err);
            return response.status(500).send("Fehler beim Speichern: " + err);
        }
    });
});

app.post('/tournament', function(request, response) {
    var tournamentID = request.body.id;
    var client = clients[request.body.uuid];
    console.log(tournamentID);
    if (tournamentID) {
        TournamentModel.findById(tournamentID, function(err, tItem) {
            if (!err) {
                if (client.tournamentID != tournamentID) {
                    client.tournamentID = tournamentID;
                    client.isAdmin = false;
                }
                socketFunctions.joinTournament(client)
                console.log('Turnier senden');
                return response.send({
                    tournament: tItem.tournament,
                    id: tItem.id,
                    isAdmin: client.isAdmin
                });
            } else {
                console.log(err);
                return response.status(404).send("Turnier nicht gefunden: " + err);
            }
        });
    } else {
        console.log('Keine ID angegeben');
        return response.status(404).send("Keine ID angegeben");
    }
});

app.post('/tournament/admin', function(request, response) {
    var tournamentID = request.body.id,
        client = clients[request.body.uuid];
    console.log(tournamentID);
    if (tournamentID) {
        TournamentModel.findById(tournamentID, function(err, tItem) {
            if (!err) {
                if (tItem.password == request.body.pwd) {
                    client.tournamentID = tournamentID;
                    client.isAdmin = true;
                    socketFunctions.joinTournament(client);
                    console.log('Turnier senden');
                    return response.send({
                        tournament: tItem.tournament,
                        id: tItem.id,
                        isAdmin: client.isAdmin
                    });
                } else {
                    return response.status(403).send('Falsches Passwort');
                }
            } else {
                console.log(err);
                return response.status(404).send("Turnier nicht gefunden: " + err);
            }
        });
    } else {
        console.log('Keine ID angegeben');
        return response.status(404).send("Keine ID angegeben");
    }
});

app.post('/tournament/new', function(request, response) {
    var client = clients[request.body.uuid];
    if (request.body.tournament && request.body.pwd) {
        //in DB speichern:
        var tournament = new TournamentModel({
            tournament: request.body.tournament,
            password: request.body.pwd,
        });
        tournament.save(function(err) {
            if (!err) {
                console.log("Gespeichert mit der ID: " + tournament._id);
                io.to('lounge').emit('refreshTournamentList', {
                    id: tournament._id,
                    name: tournament.tournament.name
                });
                client.tournamentID = tournament._id;
                client.isAdmin = true;
                socketFunctions.joinTournament(client);
                return response.send({
                    id: tournament._id,
                    isAdmin: client.isAdmin
                });
            } else {
                console.log(err);
                return response.status(500).send("Fehler beim Speichern: " + err);
            }
        });
    } else {
        return response.status(404).send("Falscher Input: " + err);
        console.log("Fehlerhafte Daten als Input");
    }

});

app.put('/tournament', function(request, response) {
    var client = clients[request.body.uuid],
        changes = request.body.changes;
    console.log("Aufruf: Set changedGames");
    if (client.isAdmin) {
        TournamentModel.findById(client.tournamentID, function(err, tObject) {
            if (!err) {
                var x,
                    tmpT = tObject.tournament;
                for (x in changes) {
                    tmpT.gameList[changes[x].index] = changes[x].game;
                }
                TournamentModel.update({
                    _id: tObject.id
                }, {
                    tournament: tmpT
                }, function(error, numberAffected, raw) {
                    if (!error) {
                        client.socket.broadcast.to(client.tournamentID).emit('gamesChanged', changes);
                        response.status(200).send('OK');
                    } else {
                        console.log(error);
                        response.status(500).send("Fehler beim Speichern: " + error);
                    }
                });
            } else {
                console.log(err);
                return response.status(500).send("Fehler beim Speichern: " + err);
            }
        });
    } else {
        return response.status(403).send('Du darfst das Turnier nicht bearbeiten.');
    }
});

app.delete('/tournament', function(request, response) {
    var client = clients[request.body.uuid];
    if (client.isAdmin) {
        var tournamentID = client.tournamentID;
        socketFunctions.leaveTournament(client);

        TournamentModel.findById(tournamentID, function(err, tObject) {
            if (!err) {
                tObject.remove(function(err) {
                    if (!err) {
                        console.log('Turnier gelöscht');
                        client.socket.broadcast.to(tournamentID).emit('gameover');
                        return response.status(200).send('OK');
                    } else {
                        console.log(err);
                        return response.status(500).send("Fehler beim Löschen: " + error);
                    }
                });
            } else {
                return response.status(500).send("Fehler beim Löschen: " + error);
                console.log(err);
            }
        });
        socketFunctions.leaveTournament(client);
        io.to('lounge').emit('refreshTournamentList', {
            id: tournamentID
        });
    } else {
        return response.status(403).send('Du darfst das Turnier nicht beenden.');
    }
});

//Start Server

io.on('connection', socketFunctions.socketHandling);

server.listen(port, function() {
    console.log('Express server listen on port %d in %s mode', port, app.settings.env);
});
